package com.tan.websocket.config;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.codec.stomp.StompSubframeDecoder;
import io.netty.handler.codec.stomp.StompSubframeEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * Netty WebSocket 服务配置类，在 Spring Boot 启动完成后启动 Netty 服务
 */


@Slf4j
@Component
public class NettyWebSocketServerConfig implements ApplicationRunner {
    /**
     * ApplicationRunner 接口，在 run 方法中进行 Netty WebSocket 服务的启动工作。同时，通过 @Autowired 注入了 WebSocketHandler 实例来添加到 Netty 的处理链中。
     */
    @Resource
    private WebSocketHandler webSocketHandler;
    @Resource
    private WebsocketAuthHandler websocketAuthHandler;

    @Value("${websocket-main.port}")
    private Integer port;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        // 创建用于接收客户端连接的事件循环组
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        // 创建用于处理客户端连接的 I/O 操作的事件循环组
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup) // 配置两组事件循环
                    .channel(NioServerSocketChannel.class) // 指定使用 NIO 的服务器套接字通道
                    .option(ChannelOption.SO_BACKLOG, 128) // 设置服务器套接字的 backlog 大小
                    .childOption(ChannelOption.SO_KEEPALIVE, true) // 开启 TCP 保活机制
                    .handler(new LoggingHandler(LogLevel.INFO)) // 添加日志处理器 (用法？)
                    .childHandler(new ChannelInitializer<SocketChannel>() { // 为子通道（客户端连接）设置初始化器
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new HttpServerCodec()); // 添加 HTTP 编解码器
                            ch.pipeline().addLast(new HttpObjectAggregator(65536)); // 聚合 HTTP 对象
//                            当您需要发送大文件或大量数据，而不希望一次性将所有数据都加载到内存中时，分块传输就非常有用。ChunkedWriteHandler 允许您逐步地将数据发送出去，而不是一次性发送整个数据块。
//                            例如，如果您要从服务器向客户端发送一个大型文件，通过使用 ChunkedWriteHandler ，可以逐块读取文件并发送，从而减少内存占用，并能够处理非常大的文件而不会导致内存溢出。
//                            总的来说，ChunkedWriteHandler 有助于提高应用在处理大体积数据传输时的性能和稳定性。
//                            ch.pipeline().addLast(new ChunkedWriteHandler());
                            ch.pipeline().addLast(websocketAuthHandler); // 添加自定义的 WebSocket 处理类
                            ch.pipeline().addLast(new WebSocketServerProtocolHandler("/tan-websocket")); // 处理 WebSocket 协议
                            // 这里设置读取空闲时间为60秒，写入空闲时间为30秒，不检测所有空闲时间
                            ch.pipeline().addLast(new IdleStateHandler(60, 10, 0, TimeUnit.SECONDS));
                            ch.pipeline().addLast(webSocketHandler); // 添加自定义的 WebSocket 处理类
                        }
                    });
            // 绑定端口 8080，并同步等待绑定完成
            ChannelFuture future = bootstrap.bind(port).sync();
            log.info("websocket服务启动成功!!!地址: localhost:{}/tan-websocket", port);
            // 等待服务器监听端口关闭
            future.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully(); // 优雅地关闭接收连接的事件循环组
            workerGroup.shutdownGracefully(); // 优雅地关闭处理 I/O 的事件循环组
        }
    }
}


